package com.jt.auth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * Oauth2,是什么?
 * Oauth2是一种协议或者规范,此规范中定义了完成用户身份认证和授权的一种方式.
 * 例如:基于用户名和密码进行认证,基于第三方(微信,QQ等)应用进行认证...
 *
 *
 * Oauth2规范中定义哪些核心对象?
 * 1.资源对象(数据库或文件中的数据)
 * 2.资源服务器对象(有你要访问的资源)
 * 3.认证管理器对象(完成用户身份的认证,完成认证后方可访问资源)
 * 4.认证客户端对象(password,第三方的(微信,QQ等进行认证...)...)
 * 5.用户对象(需要认证的对象)
 *
 *
 * 思考:
 * Oauth2规范中如何对用户身份进行认证
 * 1.提供负责认证的地址?(用户去哪里认证)
 * 2.用户携带什么资料认证?(携带的信息,例如身份证,户口本...)
 * 3.提供完成用户身份认证的对象?(AuthenticationManager)
 * 4.认证成功后颁发什么令牌?(token)
 *
 * 接下来的配置中就要完成如上几个内容
 */
@Configuration
@EnableAuthorizationServer //表示启动授权服务
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 公开认证地址(这个地址Oauth2中定义好了)
     *提供一个认证的入口(客户端去哪里认证)？(http://ip:port/.....)
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //super.configure(security);

        //公开认证地址(所有请求无需认证就可以访问这个地址)
        security //
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                //允许表单访问
                .allowFormAuthenticationForClients();
    }

    /**
     * 定义客户端认证时,需要携带的信息
     * 指明哪些对象可以到这里进行认证(哪个客户端对象需要什么特点)。
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //定义客户端标识
                .withClient("gateway-client") //这个名字是自己定义的
                //定义客户端需要携带的秘钥
                .secret(passwordEncoder.encode("123456"))
                //定义允许客户端认证的方式(基于密码方式和刷新令牌)
                .authorizedGrantTypes("password","refresh_scope")
                //指定作用域(满足如上所有条件的client都可以进行认证)
                .scopes("all");
    }

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Autowired
    private TokenStore tokenStore;
    /**
     *提供一个负责认证授权的对象？(完成客户端认证后会颁发令牌，默认令牌格式是uuid方式的)
     * UUID是随机生成的,没有业务含义,我们需要的是JWT格式的令牌
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //设置认证管理器(这个对象由spring security提供)
        endpoints.authenticationManager(authenticationManager);
        endpoints.tokenServices(tokenService());//参数为tokenService()方法的返回值
        ////设置允许对哪些请求方式进行认证(默认支支持post):可选
        //endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
    }

    @Bean
    public AuthorizationServerTokenServices tokenService(){
        //创建AuthorizationServerTokenServices类型的对象
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        //设置令牌的生成方式(令牌增强-默认是UUID)
        tokenServices.setTokenEnhancer(jwtAccessTokenConverter);//参数为jwtAccessTokenConverter()方法的返回值
        //设置令牌存储方式
        tokenServices.setTokenStore(tokenStore);
        //设置访问令牌的有效时长
        tokenServices.setAccessTokenValiditySeconds(3600);
        //设置刷新令牌以及有效时长
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setRefreshTokenValiditySeconds(3600*24);
        return tokenServices;
    }

}
